/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package wwcontrolserver.controls;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.commons.collections.map.MultiValueMap;
import wwcontrolserver.datarecorders.WWControlServerLogger;

/**
 *
 * @author Michael Ferry
 */
public class ControlManager {
    private HashMap<Integer,AbstractControl> HashOfControls = new HashMap<Integer,AbstractControl>();
    private MultiValueMap ControlKeyNotificationList = new MultiValueMap();
    private final ArrayList<AbstractControl> ListOfControls;
    private ArrayList<AbstractControl> CentralTimerNotificationList = new ArrayList<AbstractControl>();
    private final WWControlServerLogger WWCSLog;
     
    public static final int DefaultThreadPoolSize = 1;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(DefaultThreadPoolSize);   
    private ScheduledFuture<?> CentralTimer = null;
     protected static final String LogEntryDatePattern=("MM/dd/yyyy' 'HH:mm:ss");
    protected SimpleDateFormat LogEntryDateFormatter = new SimpleDateFormat(LogEntryDatePattern);
   
    
    
    public ControlManager(ArrayList<AbstractControl> ListOfControls, final WWControlServerLogger WWCSLog)throws Exception{
        //List of Controls should never be empty
        if(ListOfControls == null){
            throw new Exception();
        }
        this.ListOfControls = ListOfControls;
        //First go through all the controls and put them into a hashmap using the ControlID as a key.
        //Then for each control get an arraylist of Control keys that the control would like to be notified about
        //when some change occurs (ie an attach / detach event or sensor change).  Put these into a multivalue map

        for (AbstractControl ControlToProcess : ListOfControls) {
            HashOfControls.put(ControlToProcess.GetControlID(), ControlToProcess);
            if (ControlToProcess.GetCentralTimerNotificationState()) {
                CentralTimerNotificationList.add(ControlToProcess);
            }
            ArrayList<ControlKey> ListOfKeys = ControlToProcess.GetListOfControlKeys();
            if (ListOfKeys != null) {
                for (ControlKey CurrentKey : ListOfKeys) {
                    ControlKeyNotificationList.put(CurrentKey, ControlToProcess);
                }
            }
        }

        //This is the central timer notification thread
        final Runnable Notifier = new Runnable() {
            @Override
            public void run() {
                NotifyControlsOnCentralTimerNotificationList();
                WWCSLog.RecordLogEntry("Central notification");
                System.out.println("Central notification: " + LogEntryDateFormatter.format(System.currentTimeMillis()));
            }
        };
        //Default is control notification every minute
        CentralTimer = scheduler.scheduleAtFixedRate(Notifier, 0, 1, TimeUnit.MINUTES);
        this.WWCSLog = WWCSLog;
    }

    
    //Have this function throw an exception in the future
    public void SetControlState(AbstractControlState DesiredControlState){
        try {
            if (HashOfControls.containsKey(DesiredControlState.GetControlID())) {
                HashOfControls.get(DesiredControlState.GetControlID()).SetControlState(DesiredControlState);
            }
        } catch (Exception e) {
            WWCSLog.RecordExceptionLogEntry(e);
        }
    }
    //Have this function throw an exception in the future
    public void SetControlState(int ControlID, AbstractControlState DesiredControlState) {
        try {
            if (HashOfControls.containsKey(ControlID)) {
                HashOfControls.get(ControlID).SetControlState(DesiredControlState);
            }
        } catch (Exception e) {
            WWCSLog.RecordExceptionLogEntry(e);
        }
    }
    //Note may return null
    public AbstractControlState GetControlState(int ControlID){
        return HashOfControls.get(ControlID).GetControlState();
    }
    //this is generally used to notify controls of attach and detach events
    public void NotifyControlsUsingKey(ControlKey KeyForNotification){
        //First determine if any controls want to be notified of this event
        if (ControlKeyNotificationList.containsKey(KeyForNotification)) {
            ArrayList<AbstractControl> ControlsToProcess = new ArrayList<AbstractControl>((ControlKeyNotificationList.getCollection(KeyForNotification)));
            for (AbstractControl ControlToProcess : ControlsToProcess) {
                ControlToProcess.NotifyControl();
            }
        }
    }
    public ArrayList<AbstractControlState> GetStateOfAllControls(){
        ArrayList<AbstractControlState> ControlStateList = new ArrayList<AbstractControlState>();
        for (AbstractControl ControlToProcess : ListOfControls) {
            ControlStateList.add(ControlToProcess.GetControlState());
        }
        return ControlStateList;
    }

    public void NotifyAllControls(){
        for (AbstractControl ControlToProcess : ListOfControls) {
            ControlToProcess.NotifyControl();
        }
    }
    public void NotifyControlsOnCentralTimerNotificationList(){
        for (AbstractControl ControlToProcess : CentralTimerNotificationList) {
            ControlToProcess.NotifyControl();
        }
    }
    private void ShutdownAllControls(){
        for (AbstractControl ControlToProcess : ListOfControls) {
            ControlToProcess.ShutdownControl();
        }
    }
    public void ShutdownControlManager(){
        CentralTimer.cancel(true);
        scheduler.shutdown();
        ShutdownAllControls();
    }
    
}

